home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 November
/
Macworld (1999-11).dmg
/
Updaters
/
WhiteCap 3.0.4
/
WhiteCap Source.sit
/
WhiteCap Source
/
Common
/
math
/
ExprVirtualMachine.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1999-07-28
|
11KB
|
461 lines
#include "ExprVirtualMachine.h"
#include <math.h>
#include <stdlib.h>
#define __addInst( opcode, data16 ) long op = (opcode) | (data16); \
mProgram.Append( &op, 4 );
#define REG_IN_USE 0x1
#define REG_USED 0x2
#define _fetch( r, val ) switch ( (r) ) { \
case 0: val = FR0; break; \
case 1: val = FR1; break; \
case 2: val = FR2; break; \
case 3: val = FR3; break; \
case 4: val = FR4; break; \
case 5: val = FR5; break; \
case 6: val = FR6; break; \
case 7: val = FR7; break; \
default: val = mVirtFR[ r - NUM_PHYS_REGS ]; \
}
/*
case 8: val = FR8; break; \
case 9: val = FR9; break; \
case 10: val = FR10; break; \
case 11: val = FR11; break; \
case 12: val = FR12; break; \
case 13: val = FR13; break; \
case 14: val = FR14; break; \
case 15: val = FR15; break; \
default: val = 0; break; \
}*/
#define _store( r, val ) switch ( (r) ) { \
case 0: FR0 = val; break; \
case 1: FR1 = val; break; \
case 2: FR2 = val; break; \
case 3: FR3 = val; break; \
case 4: FR4 = val; break; \
case 5: FR5 = val; break; \
case 6: FR6 = val; break; \
case 7: FR7 = val; break; \
default: mVirtFR[ r - NUM_PHYS_REGS ] = val; \
}
/*
case 8: FR8 = val; break; \
case 9: FR9 = val; break; \
case 10: FR10 = val; break; \
case 11: FR11 = val; break; \
case 12: FR12 = val; break; \
case 13: FR13 = val; break; \
case 14: FR14 = val; break; \
case 15: FR15 = val; break; \
}*/
#define _exeFn( r ) switch ( subop ) { \
case cSQRT: r = sqrt( r ); break; \
case cATAN: r = atan( r ); break; \
case cABS: r = fabs( r ); break; \
case cSIN: r = sin( r ); break; \
case cCOS: r = cos( r ); break; \
case cTAN: r = tan( r ); break; \
case cSGN: r = ( r >= 0 ) ? 1 : -1; break; \
case cLOG: r = log( r ); break; \
case cEXP: r = exp( r ); break; \
case cSQR: r = r * r; break; \
case cRND: r = r * ( (float) rand() ) / ( (float) RAND_MAX ); break; \
case cSQWV: r = ( r >= -1.0 && r <= 1.0 ) ? 1 : 0; break; \
case cTRWV: r = fabs( .5 * r ); r = 2 * ( r - floor( r ) ); if ( r > 1 ) r = 2 - r; break; \
case cPOS: if ( r < 0 ) r = 0; break; \
case cSEED: i = *((long*) &r); \
size = i % 31; \
srand( ( i << size ) | ( i >> ( 32 - size ) ) ); break; \
case cCLIP: if ( r < 0 ) r = 0; else if ( r > 1 ) r = 1; break; \
}
#define _exeOp( r1, r2 ) switch ( subop ) { \
case '+': r1 += r2; break; \
case '-': r1 -= r2; break; \
case '/': r1 /= r2; break; \
case '*': r1 *= r2; break; \
case '^': r1 = pow( r1, r2 ); break; \
case '%': r1 = ( (long) r1 ) % ( (long) r2 ); break; \
}
ExprVirtualMachine::ExprVirtualMachine() {
mPCStart = NULL;
mPCEnd = NULL;
}
int ExprVirtualMachine::FindGlobalFreeReg() {
int reg = 1;
// Look for a global free register
while ( ( mRegColor[ reg ] & REG_USED ) && reg < NUM_REGS )
reg++;
return reg;
}
int ExprVirtualMachine::AllocReg() {
int reg = 0;
// Look for a free register (ie, find one not in use right now)...
while ( ( mRegColor[ reg ] & REG_IN_USE ) && reg < NUM_REGS )
reg++;
// Color it
if ( reg < NUM_REGS )
mRegColor[ reg ] = REG_IN_USE | REG_USED;
return reg;
}
void ExprVirtualMachine::DeallocReg( int inReg ) {
// Clear the bit that says the reg is allocated
mRegColor[ inReg ] &= ~REG_IN_USE;
}
void ExprVirtualMachine::DoOp( int inReg, int inReg2, char inOpCode ) {
__addInst( OP_OPER, ( inOpCode << 16 ) | ( inReg2 << 8 ) | inReg )
}
void ExprVirtualMachine::Move( int inReg, int inDestReg ) {
if ( inDestReg != inReg ) {
__addInst( OP_MOVE, ( inReg << 8 ) | inDestReg )
}
}
void ExprVirtualMachine::Loadi( float inVal, int inReg ) {
__addInst( OP_LOADIMMED, inReg )
mProgram.Append( &inVal, sizeof( float ) );
}
void ExprVirtualMachine::Loadi( float* inVal, int inReg ) {
__addInst( OP_LOAD, inReg )
mProgram.Append( &inVal, 4 );
}
void ExprVirtualMachine::UserFcnOp( int inReg, float** inFcnH, long inSize ) {
unsigned short size = inSize;
if ( inFcnH ) {
__addInst( OP_USER_FCN, inReg )
mProgram.Append( &size, 2 );
mProgram.Append( &inFcnH, 4 ); }
else
Loadi( 0.0, inReg );
}
void ExprVirtualMachine::MathOp( int inReg, char inFcnCode ) {
if ( inFcnCode ) {
__addInst( OP_MATHOP, ( inFcnCode << 16 ) | inReg )
}
}
void ExprVirtualMachine::Clear() {
// Init register coloring
for ( int i = 0; i < NUM_REGS; i++ )
mRegColor[ i ] = 0;
mProgram.Wipe();
}
void ExprVirtualMachine::PrepForExecution() {
mPCStart = mProgram.getCStr();
mPCEnd = mPCStart + mProgram.length();
}
// An inst looks like:
// 0-7: Inst opcode
// 8-15: Sub opcode
// 16-23: Source Reg
// 24-31: Dest Register number
float ExprVirtualMachine::Execute/*_Inline*/() {
register float FR0, FR1, FR2, FR3, FR4, FR5, FR6, FR7; // FR8, FR9, FR10, FR11, FR12, FR13, FR14, FR15;
register float v1, v2;
const char* PC = mPCStart;
const char* end = mPCEnd;
unsigned long inst, opcode, subop, size, i;
float* fcnBase;
float mVirtFR[ NUM_REGS - NUM_PHYS_REGS ];
while ( PC < end ) {
inst = *((long*) PC);
PC += 4;
opcode = inst & 0xFF000000;
if ( opcode == OP_LOADIMMED ) {
v1 = *((float*) PC);
PC += 4; }
else if ( opcode == OP_LOAD ) {
v1 = **((float**) PC);
PC += 4; }
else {
_fetch( inst & 0xFF, v1 )
_fetch( ( inst >> 8 ) & 0xFF, v2 )
subop = ( inst >> 16 ) & 0xFF;
switch ( opcode ) {
case OP_OPER:
_exeOp( v1, v2 )
break;
case OP_MATHOP:
_exeFn( v1 )
break;
case OP_USER_FCN:
size = *( (unsigned short*) PC );
PC += 2;
fcnBase = **((float***) PC);
PC += 4;
i = v1 * size;
if ( i < 0 )
i = 0;
if ( i >= size )
i = size - 1;
v1 = fcnBase[ i ];
break;
case OP_MOVE:
v1 = v2;
break;
case OP_WEIGHT:
float weight = **((float**) PC);
PC += 4;
v1 = ( 1.0 - weight ) * v1 + weight * v2;
break;
}
}
_store( inst & 0xFF, v1 )
}
return FR0;
}
void ExprVirtualMachine::Chain( ExprVirtualMachine& inVM, float* inTransitionLink ) {
int tempReg = inVM.FindGlobalFreeReg();
// Move the output of this VM to a reg that won't get overwritten by inVM
Move( 0, tempReg );
// Now execute inVM (we know it won't touch tempReg)
mProgram.Append( inVM.mProgram );
// Use the special weight op that combines the two outputs via an embedded addr to a 0 to 1 value
// Note that the output is moved to register 0
__addInst( OP_WEIGHT, ( tempReg << 8 ) | 0 )
mProgram.Append( &inTransitionLink, 4 );
// The reg coloring for this VM is the OR of the two's coloring
for ( int i = 0; i < NUM_REGS; i++ )
mRegColor[ i ] |= inVM.mRegColor[ i ];
PrepForExecution();
}
void ExprVirtualMachine::Assign( ExprVirtualMachine& inExpr ) {
mProgram.Assign( inExpr.mProgram );
for ( int i = 0; i < NUM_REGS; i++ )
mRegColor[ i ] = inExpr.mRegColor[ i ];
PrepForExecution();
}
/*
void ExprVirtualMachine::Neg() {
__addInst( OP_NEG, 0 )
}
*/
/*
void ExprVirtualMachine::Optimize() {
char* base = mProgram.getCStr();
long* PC = (long*) mProgram.getCStr();
long* end = (long*) (((char*) PC) + mProgram.length());
long reg, opcode;
long* start;
while ( PC < end ) {
opcode = (*PC) & 0xFF000000;
start = PC;
PC++;
// Maintain the PC
if ( opcode == OP_LOADIMMED )
PC += 2;
else if ( opcode == OP_LOAD )
PC++;
// Look for a 'Load into r0, <Math op>, Move from r0' sequence
if ( opcode == OP_LOADIMMED || opcode == OP_LOAD ) {
opcode = (*PC) & 0xFF000000;
if ( opcode == OP_MOVE_FR0 ) {
reg = *PC & 0xFF; // Extract the final dest register
*start = (*start) | reg; // Change the load so it loads right into the reg it needs to
mProgram.Remove( 1 + ( ((char*) PC) - base ), 4 ); } // Delete the move from fr0 inst
// ??
else if ( opcode == OP_MATHOP ) {
opcode = (*(PC + 1)) & 0xFF000000;
if ( opcode == OP_MOVE_FR0 ) {
reg = *(PC + 1) & 0xFF; // Extract the final dest register
*start = (*start) | reg; // Change the load so it loads right into the reg it needs to
*PC = (*PC) | reg; // Change the math op so it operates on the proper reg (see above)
mProgram.Remove( 5 + ( ((char*) PC) - base ), 4 ); // Delete the move from fr0 inst
}
} // ??
}
}
// Minimzing pushes/pops via stack analysis
StackReduction( 0, mProgram.length() );
}
long ExprVirtualMachine::StackReduction( long inStartPC, long inEndPC ) {
long regsInUse = 0, opcode, fcnDepth = 0, pushLoc, reg, regsToPush;
long PC = inStartPC, progLen, subRegs, *inst;
char* base = mProgram.getCStr();
while ( PC < inEndPC ) {
reg = *((long*) (PC + base));
opcode = reg & 0xFF000000; // Extract the opcode
reg &= 0xFF; // Extract the dest reg
// We're only interested in root level pop/pushes (ie, when fcnDepth == 0)
switch ( opcode ) {
case OP_MASSPUSH:
if ( fcnDepth == 0 ) {
pushLoc = PC;
regsInUse = reg; // We know what's in use by what the compiler wanted us to push
}
fcnDepth++;
break;
case OP_MASSPOP:
fcnDepth--;
break;
case OP_LOADIMMED:
PC += 4;
case OP_LOAD:
PC += 4;
}
// see what regs are in use--skip over insts not at the root level
if ( fcnDepth == 0 ) {
switch ( opcode ) {
case OP_OPER:
regsInUse |= ( 2 << reg );
case OP_LOADIMMED:
case OP_LOAD:
case OP_MATHOP:
case OP_MOVEUP:
case OP_MOVE_FR0:
regsInUse |= ( 1 << reg );
break;
// Catch the leaving a fcn at the root level
case OP_MASSPOP:
// Get the regs that get sub-used (ie, used between pushLoc and PC)
progLen = mProgram.length();
subRegs = StackReduction( pushLoc + 4, PC );
// StackReduction() may have elminated instructions, so adjust our PC
PC -= progLen - mProgram.length();
inEndPC -= progLen - mProgram.length();
// Reassign what regs get pushed then popped. We must push the regs that get used in the sub fcn and we use here
regsToPush = subRegs & regsInUse;
if ( regsToPush ) {
inst = (long*) (base + pushLoc);
*inst = OP_MASSPUSH | regsToPush; // Reassign the push
inst = (long*) (base + PC);
*inst = OP_MASSPOP | regsToPush; } // Reassign the pop
// If no regs need to get pushed, delete the pop and push insts
else {
mProgram.Remove( PC + 1, 4 );
mProgram.Remove( pushLoc + 1, 4 );
PC -= 8;
inEndPC -= 8;
}
break;
}
}
// Move the PC along, for we just looked at an instruction
PC += 4;
}
return regsInUse;
}
*/